﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web.Mvc;
using System.Web.Security;

using ApplicationBoilerplate.DataProvider;

using MVCBootstrap.Web.Security;

using MongoDB.Driver.Builders;

namespace MVCBootstrap.MongoDB.Web.Security {

	public class MongoDBMembershipProvider : GenericMembershipProvider {
		private IConnection conn;

		private IConnection Connection {
			get {
				if (this.conn == null) {
					this.conn = DependencyResolver.Current.GetService<IConnection>() as Connection;
				}
				return this.conn;
			}
		}

		protected override IEnumerable<IUser> GetUsersByName(String name) {
			return this.Connection
					.GetRepository<User>()
					.ReadMany(u => u.UpperUsername.Contains(name.ToUpper()) && u.ApplicationName == this.ApplicationName);
		}

		protected override IEnumerable<IUser> GetUsersByEmail(String email) {
			return this.Connection
					.GetRepository<User>()
					.ReadMany(u => u.Email.Contains(email.ToLower()) && u.ApplicationName == this.ApplicationName);
		}

		protected override Int32 GetOnlineUsers() {
			TimeSpan timeSpan = TimeSpan.FromMinutes(Membership.UserIsOnlineTimeWindow);
			return this.Connection
					.GetRepository<User>()
					.ReadMany(u => u.ApplicationName == this.ApplicationName && u.LastActivityDate > DateTime.UtcNow.Subtract(timeSpan))
					.Count();
		}

		protected override IEnumerable<IUser> GetAllUsersInternal() {
			return this.Connection
					.GetRepository<User>()
					.ReadMany(u => u.ApplicationName == this.ApplicationName);
		}

		protected override IUser GetUserByEmail(String email) {
			return this.Connection
					.GetRepository<User>()
					.ReadOne(u => u.ApplicationName == this.ApplicationName && u.Email == email.ToLower());
		}

		protected override IUser GetUserByName(String username) {
			return this.Connection
					.GetRepository<User>()
					.ReadOne(u => u.ApplicationName == this.ApplicationName && u.UpperUsername == username.ToUpper());
		}

		protected override IUser GetUserByProviderId(Guid id) {
			return this.Connection
					.GetRepository<User>()
					.ReadOne(u => u.ApplicationName == this.ApplicationName && u.Id == id);
		}

		protected override Boolean UpdateUser(IUser user) {
			User u = user as User;
			if (u == null) {
				throw new ArgumentException("user");
			}
			// Make sure the e-mail address is in lower-case!
			u.Email = user.Email.ToLower();
			// Make sure the username is stored in upper-case too, so we can search after it!
			u.UpperUsername = user.Username.ToUpper();
			this.Connection.GetRepository<User>().Update(u);
			return true;
		}

		protected override void CreateUser(Guid id, DateTime creationDate, String emailAddress, Boolean isApproved, String password, String passwordAnswer, String passwordQuestion, String salt, String username) {
			User user = new User {
				Id = id,
				ApplicationName = this.ApplicationName,
				CreationDate = creationDate,
				Email = emailAddress.ToLower(),
				FailedPasswordAnswerAttemptCount = 0,
				FailedPasswordAttemptWindowStart = creationDate,
				FailedPasswordAttemptCount = 0,
				FailedPasswordAnswerAttemptWindowStart = creationDate,
				IsApproved = isApproved,
				IsLockedOut = false,
				LastActivityDate = creationDate,
				LastLockoutDate = new DateTime(1970, 1, 1),
				LastLoginDate = creationDate,
				LastPasswordChangedDate = creationDate,
				Password = this.EncodePassword(password, this.PasswordFormat, salt),
				PasswordAnswer = this.EncodePassword(passwordAnswer, this.PasswordFormat, salt),
				PasswordQuestion = passwordQuestion,
				Salt = salt,
				Username = username,
				UpperUsername = username.ToUpper(),
				Comment = String.Empty
			};

			this.Connection.GetRepository<User>().Create(user);
		}

		protected override Boolean DeleteUserInternal(String username, Boolean deleteAllRelatedData) {
			// We could use the generic repository, but this is faster!
			return this.Connection
					.GetCollection<User>()
					.GetCollection()
					.Remove(Query.EQ("uusername", username.ToUpper()))
					.Ok;
			//this.Context.GetRepository<User>().Delete(this.GetUserByName(username).Id);
			//return true;
		}
	}
}